Client and Subscriber Development > Extensions > Integrated Extensions > OPC UA Complex Data Extension > Generic data and data types > Working with generic data > Processing generic data |
In order to process generic data (a sub-type of GenericData Class), you need to first determine its kind (see Generic data kinds). You can do it by
All generic data classes share a common DataType Property, which contains an optional data type object.
After you have determined the kind of generic data you are dealing with, you can use the generic data object cast to appropriate type, and work with properties and methods that are specific to that data type kind. For example, SequenceData contains the elements of the sequence in its Elements Property.
The following example shows how generic data can be processed.
// Shows how to process generic data type, displaying some of its properties, recursively. // // Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . using System; using System.Collections.Generic; using OpcLabs.BaseLib.DataTypeModel; using OpcLabs.EasyOpc.UA; using OpcLabs.EasyOpc.UA.ComplexData; using OpcLabs.EasyOpc.UA.OperationModel; namespace UADocExamples.ComplexData._GenericData { class DataTypeKind1 { public static void Main1() { // Define which server and node we will work with. UAEndpointDescriptor endpointDescriptor = "opc.tcp://opcua.demo-this.com:51210/UA/SampleServer"; // or "http://opcua.demo-this.com:51211/UA/SampleServer" (currently not supported) // or "https://opcua.demo-this.com:51212/UA/SampleServer/" UANodeDescriptor nodeDescriptor = "nsu=http://test.org/UA/Data/ ;i=10239"; // [ObjectsFolder]/Data.Static.Scalar.StructureValue // Instantiate the client object. var client = new EasyUAClient(); // Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. UAGenericObject genericObject; try { genericObject = (UAGenericObject)client.ReadValue(endpointDescriptor, nodeDescriptor); } catch (UAException uaException) { Console.WriteLine("*** Failure: {0}", uaException.GetBaseException().Message); return; } // Process the generic data type. We will inspect some of its properties, and dump them. ProcessGenericData(genericObject.GenericData, maximumDepth: 3); } // Process the generic data type. Its structure can sometimes be quite deep, therefore we are limiting the depth // of the recursion using maximumDepth. public static void ProcessGenericData(GenericData genericData, int maximumDepth) { if (maximumDepth == 0) return; Console.WriteLine(); Console.WriteLine("genericData.DataType: {0}", genericData.DataType); switch (genericData.DataTypeKind) { case DataTypeKind.Enumeration: Console.WriteLine("The generic data is an enumeration."); var enumerationData = (EnumerationData) genericData; Console.WriteLine("Its value is {0}.", enumerationData.Value); // There is also a ValueName that you can inspect (if known). break; case DataTypeKind.Opaque: Console.WriteLine("The generic data is opaque."); var opaqueData = (OpaqueData) genericData; Console.WriteLine("Its size is {0} bits.", opaqueData.SizeInBits); Console.WriteLine("The data bytes are {0}.", BitConverter.ToString(opaqueData.ByteArray)); // Use the Value property (a BitArray) if you need to access the value bit by bit. break; case DataTypeKind.Primitive: Console.WriteLine("The generic data is primitive."); var primitiveData = (PrimitiveData) genericData; Console.WriteLine("Its value is \"{0}\".", primitiveData.Value); break; case DataTypeKind.Sequence: Console.WriteLine("The generic data is a sequence."); var sequenceData = (SequenceData) genericData; Console.WriteLine("It has {0} elements.", sequenceData.Elements.Count); Console.WriteLine("A dump of the elements follows."); foreach (GenericData element in sequenceData.Elements) ProcessGenericData(element, maximumDepth - 1); break; case DataTypeKind.Structured: Console.WriteLine("The generic data is structured."); var structuredData = (StructuredData) genericData; Console.WriteLine("It has {0} field data members.", structuredData.FieldData.Count); Console.WriteLine("The names of the fields are: {0}.", String.Join(", ", structuredData.FieldData.Keys)); Console.WriteLine("A dump of each of the fields follows."); foreach (KeyValuePair<string, GenericData> pair in structuredData.FieldData) { Console.WriteLine(); Console.WriteLine("Field name: {0}", pair.Key); ProcessGenericData(pair.Value, maximumDepth - 1); } break; case DataTypeKind.Union: Console.WriteLine("The generic data is a union."); var unionData = (UnionData)genericData; Console.WriteLine("The name of current field is: {0}", unionData.FieldName); Console.WriteLine("Current field value is: {0}", unionData.FieldValue); break; } } } }
# Shows how to process generic data type, displaying some of its properties, recursively. # # Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . # OPC client and subscriber examples in Python on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-Python . # The QuickOPC package is needed. Install it using "pip install opclabs_quickopc". import opclabs_quickopc # Import .NET namespaces. from System import * from OpcLabs.BaseLib.DataTypeModel import * from OpcLabs.EasyOpc.UA import * from OpcLabs.EasyOpc.UA.OperationModel import * def processGenericData(genericData, maximumDepth): if maximumDepth == 0: print('* Reached maximum depth *') return print() print('genericData.DataType: ', genericData.DataType, sep='') dataTypeKind = genericData.DataTypeKind if dataTypeKind == DataTypeKind.Enumeration: print('The generic data is an enumeration.') enumerationData = genericData print('Its value is ', enumerationData.Value, '.', sep='') # There is also a ValueName that you can inspect (if known). elif dataTypeKind == DataTypeKind.Opaque: print('The generic data is opaque.') opaqueData = genericData print('Its size is ', opaqueData.SizeInBits, ' bits.', sep='') print('The data bytes are ', BitConverter.ToString(opaqueData.ByteArray), '.', sep='') # Use the Value property (a BitArray) if you need to access the value bit by bit. elif dataTypeKind == DataTypeKind.Primitive: print('The generic data is primitive.') primitiveData = genericData print('Its value is "', primitiveData.Value, '".', sep='') elif dataTypeKind == DataTypeKind.Sequence: print('The generic data is a sequence.') sequenceData = genericData print('It has ', sequenceData.Elements.Count, ' elements.', sep='') print('A dump of the elements follows.') for element in sequenceData.Elements: processGenericData(element, maximumDepth - 1) elif dataTypeKind == DataTypeKind.Structured: print('The generic data is structured.') structuredData = genericData print('It has ', structuredData.FieldData.Count, ' field data members.', sep='') print('The names of the fields are: ', end='') for i, name in enumerate(structuredData.FieldData.Keys): if i != 0: print(', ', end='') print(name, end='') print('.') print('A dump of each of the fields follows.') for pair in structuredData.FieldData: print() print('Field name: ', pair.Key, sep='') processGenericData(pair.Value, maximumDepth - 1) elif dataTypeKind == DataTypeKind.Union: print('The generic data is a union.') unionData = genericData print('The name of current field is: ', unionData.FieldName, sep='') print('Current field value is: ', unionData.FieldValue, end='') # Define which server and node we will work with. endpointDescriptor = UAEndpointDescriptor('opc.tcp://opcua.demo-this.com:51210/UA/SampleServer') # or 'http://opcua.demo-this.com:51211/UA/SampleServer' (currently not supported) # or 'https://opcua.demo-this.com:51212/UA/SampleServer/' # [ObjectsFolder]/Data.Static.Scalar.StructureValue nodeDescriptor = UANodeDescriptor('nsu=http://test.org/UA/Data/ ;i=10239') # Instantiate the client object. client = EasyUAClient() # Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. try: print('Reading...') genericObject = IEasyUAClientExtension.ReadValue(client, endpointDescriptor, nodeDescriptor) except UAException as uaException: print('*** Failure: ' + uaException.GetBaseException().Message) exit() print('Reading successful.') # Process the generic data type. We will inspect some of its properties, and dump them. processGenericData(genericObject.GenericData, 3) print() print('Finished.')
' Shows how to process generic data type, displaying some of its properties, recursively. ' ' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . Imports OpcLabs.BaseLib.DataTypeModel Imports OpcLabs.EasyOpc.UA Imports OpcLabs.EasyOpc.UA.ComplexData Imports OpcLabs.EasyOpc.UA.OperationModel Namespace ComplexData._GenericData Friend Class DataTypeKind1 Public Shared Sub Main1() ' Define which server we will work with. Dim endpointDescriptor As UAEndpointDescriptor = "opc.tcp://opcua.demo-this.com:51210/UA/SampleServer" ' or "http://opcua.demo-this.com:51211/UA/SampleServer" (currently not supported) ' or "https://opcua.demo-this.com:51212/UA/SampleServer/" ' Define which node we will work with. Dim nodeDescriptor As UANodeDescriptor = _ "nsu=http://test.org/UA/Data/ ;i=10239" ' [ObjectsFolder]/Data.Static.Scalar.StructureValue ' Instantiate the client object. Dim client = New EasyUAClient ' Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. Dim genericObject As UAGenericObject Try genericObject = CType(client.ReadValue(endpointDescriptor, nodeDescriptor), UAGenericObject) Catch uaException As UAException Console.WriteLine("*** Failure: {0}", uaException.GetBaseException.Message) Exit Sub End Try ' Process the generic data type. We will inspect some of its properties, and dump them. ProcessGenericData(genericObject.GenericData, maximumDepth:=2) End Sub ' Process the generic data type. Its structure can sometimes be quite deep, therefore we are limiting the depth ' of the recursion using maximumDepth. Public Shared Sub ProcessGenericData(ByVal genericData As GenericData, ByVal maximumDepth As Integer) If (maximumDepth = 0) Then Return End If Console.WriteLine() Console.WriteLine("genericData.DataType: {0}", genericData.DataType) Select Case (genericData.DataTypeKind) Case DataTypeKind.Enumeration Console.WriteLine("The generic data is an enumeration.") Dim enumerationData = CType(genericData, EnumerationData) Console.WriteLine("Its value is {0}.", enumerationData.Value) ' There is also a ValueName that you can inspect (if known). Case DataTypeKind.Opaque Console.WriteLine("The generic data is opaque.") Dim opaqueData = CType(genericData, OpaqueData) Console.WriteLine("Its size is {0} bits.", opaqueData.SizeInBits) Console.WriteLine("The data bytes are {0}.", BitConverter.ToString(opaqueData.ByteArray)) ' Use the Value property (a BitArray) if you need to access the value bit by bit. Case DataTypeKind.Primitive Console.WriteLine("The generic data is primitive.") Dim primitiveData = CType(genericData, PrimitiveData) Console.WriteLine("Its value is ""{0}"".", primitiveData.Value) Case DataTypeKind.Sequence Console.WriteLine("The generic data is a sequence.") Dim sequenceData = CType(genericData, SequenceData) Console.WriteLine("It has {0} elements.", sequenceData.Elements.Count) Console.WriteLine("A dump of the elements follows.") For Each element As GenericData In sequenceData.Elements ProcessGenericData(element, (maximumDepth - 1)) Next Case DataTypeKind.Structured Console.WriteLine("The generic data is structured.") Dim structuredData = CType(genericData, StructuredData) Console.WriteLine("It has {0} field data members.", structuredData.FieldData.Count) Console.WriteLine("The names of the fields are: {0}.", String.Join(", ", structuredData.FieldData.Keys)) Console.WriteLine("A dump of each of the fields follows.") For Each pair As KeyValuePair(Of String, GenericData) In structuredData.FieldData Console.WriteLine() Console.WriteLine("Field name: {0}", pair.Key) ProcessGenericData(pair.Value, (maximumDepth - 1)) Next Case DataTypeKind.Union Console.WriteLine("The generic data is union.") Dim unionData = CType(genericData, UnionData) Console.WriteLine("The name of current field is: {0}", unionData.FieldName) Console.WriteLine("Current field value is: {0}", unionData.FieldValue) End Select End Sub End Class End Namespace
// Shows how to process generic data type, displaying some of its properties, recursively // // Find all latest examples here : https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . class procedure DataTypeKind1.Main; var Client: _EasyUAClient; EndpointDescriptor: string; GenericObject: _UAGenericObject; NodeDescriptor: string; begin EndpointDescriptor := //'http://opcua.demo-this.com:51211/UA/SampleServer'; //'https://opcua.demo-this.com:51212/UA/SampleServer/'; 'opc.tcp://opcua.demo-this.com:51210/UA/SampleServer'; NodeDescriptor := 'nsu=http://test.org/UA/Data/ ;i=10239'; // [ObjectsFolder]/Data.Static.Scalar.StructureValue // Instantiate the client object Client := CoEasyUAClient.Create; // Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. try GenericObject := _UAGenericObject(IUnknown(Client.ReadValue(EndpointDescriptor, NodeDescriptor))); except on E: EOleException do begin WriteLn(Format('*** Failure: %s', [E.GetBaseException.Message])); Exit; end; end; // Process the generic data type. We will inspect some of its properties, and dump them. ProcessGenericData(GenericObject.GenericData, 2); end; Function VariantToBytes(Const Value: OleVariant): TBytes; Var Size: Integer; pData: Pointer; Begin Size := Succ(VarArrayHighBound(Value, 1) - VarArrayLowBound(Value, 1)); SetLength(Result, Size); pData := VarArrayLock(Value); Try Move(pData^, Pointer(Result)^, Size); Finally VarArrayUnlock(Value); End; End; class procedure DatatypeKind1.ProcessGenericData(GenericData: OpcLabs_BaseLib_TLB._GenericData; MaximumDepth: Cardinal); var ByteArray: OleVariant; Count: Cardinal; Element: OleVariant; ElementEnumerator: IEnumVARIANT; EnumerationData: _EnumerationData; First: boolean; Keys: string; OpaqueData: _OpaqueData; PrimitiveData: _PrimitiveData; SequenceData: _SequenceData; StructuredData: _StructuredData; Value: OpcLabs_BaseLib_TLB._GenericData; begin if MaximumDepth = 0 then Exit; WriteLn; WriteLn('genericData.DataType: ', GenericData.DataType.ToString); case GenericData.DataTypeKind of DataTypeKind_Enumeration: begin WriteLn('The generic data is an enumeration.'); EnumerationData := GenericData as _EnumerationData; WriteLn(Format('Its value is %s.', [EnumerationData.Value.ToString])); // There is also a ValueName that you can inspect (if known). end; DataTypeKind_Opaque: begin WriteLn('The generic data is opaque.'); OpaqueData := GenericData as _OpaqueData; WriteLn(Format('Its size is %d bits.', [OpaqueData.SizeInBits])); TVarData(ByteArray).VType := varArray; TVarData(ByteArray).VArray := PVarArray(OpaqueData.ByteArray); WriteLn(Format('The data bytes are %s.', [TEncoding.ANSI.GetString(VariantToBytes(ByteArray))])); // Use the Value property (a BitArray) if you need to access the value bit by bit. end; DataTypeKind_Primitive: begin WriteLn('The generic data is primitive.'); PrimitiveData := GenericData as _PrimitiveData; WriteLn(Format('Its value is "%s".', [PrimitiveData.Value])); end; DataTypeKind_Sequence: begin WriteLn('The generic data is a sequence.'); SequenceData := GenericData as _SequenceData; WriteLn(Format('It has %s elements.', [SequenceData.Elements.Count.ToString])); WriteLn('A dump of the elements follows.'); ElementEnumerator := SequenceData.Elements.GetEnumerator; while (ElementEnumerator.Next(1, Element, Count) = S_OK) do begin ProcessGenericData(IUnknown(Element) as OpcLabs_BaseLib_TLB._GenericData, MaximumDepth - 1); end; end; DataTypeKind_Structured: begin WriteLn('The generic data is structured.'); StructuredData := GenericData as _StructuredData; WriteLn(Format('It has %s field data members.', [StructuredData.FieldData.Count.ToString])); ElementEnumerator := StructuredData.FieldData.GetEnumerator; Keys := ''; First := True; while (ElementEnumerator.Next(1, Element, Count) = S_OK) do begin if First then First := False else Keys := Keys + ', '; Keys := Keys + Element.Key; end; WriteLn(Format('The names of the fields are: %s.', [Keys])); WriteLn('A dump of each of the fields follows.'); ElementEnumerator := StructuredData.FieldData.GetEnumerator; while (ElementEnumerator.Next(1, Element, Count) = S_OK) do begin WriteLn; WriteLn(Format('Field name: %s', [Element.Key])); Value := IUnknown(Element.Value) as OpcLabs_BaseLib_TLB._GenericData; ProcessGenericData(Value, MaximumDepth - 1); end; end; end; end;
In many cases, you will not be processing the generic data alone, but in parallel with the data type. See Processing data types for details.
Copyright © 2004-2024 CODE Consulting and Development, s.r.o., Plzen. All rights reserved. Web page: www.opclabs.com
Send Documentation Feedback. Resources: Knowledge Base, Product Downloads. Technical support: Online Forums, FAQ.Missing some example? Ask us for it on our Online Forums! You do not have to own a commercial license in order to use Online Forums, and we reply to every post.